

//
// Created by guts on 6/24/21.
//
#include "CarPlayInterface.h"
#include "DebugServices.h"

#define DEBUG_CI                1

ulog_define(CarPlayInterface, kLogLevelTrace, kLogFlags_Default, "CarPlayInterface", NULL);
#define ci_ucat()               &log_category_from_name( CarPlayInterface )
#define ci_ulog(LEVEL, ...)     ulog( ci_ucat(), (LEVEL), __VA_ARGS__ )
#define ci_dlog(LEVEL, ...)     dlogc( ci_ucat(), (LEVEL), __VA_ARGS__ )

static UserInterfaceDelegate gUserInterfaceDelegate;
static CarPlayInterfaceDelegate gCarPlayInterfaceDelegate;
static iAP2InterfaceDelegate giAP2InterfaceDelegate;

//static Airplay *                gAirPlaySkeletonObject;
//static Iap2 *                   giAP2SkeletonObject;
char            gRfcommReceivedBuffer[1024];

//=================================================================================================================
// register CarPlayInterface
//=================================================================================================================
void CarPlayInterfaceSetDelegate(const CarPlayInterfaceDelegate* inDelegate) {
    gCarPlayInterfaceDelegate = *inDelegate;
}

void UserInterfaceSetDelegate(const UserInterfaceDelegate* inDelegate) {
	gUserInterfaceDelegate = *inDelegate;
}

//=================================================================================================================
// register iAP2Interface
//=================================================================================================================
void iAP2InterfaceSetDelegate(const iAP2InterfaceDelegate* inDelegate) {
    giAP2InterfaceDelegate = *inDelegate;
}


//=================================================================================================================
// interface com.hhh.airplay
//=================================================================================================================
bool sendEnd(void) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendEnd command.\n");
#endif

    gCarPlayInterfaceDelegate.end_f();

    return true;
}


bool sendInitialModesCommand(const char* arg_command) {
#if (DEBUG_CI )
    ci_ulog(kLogLevelNotice, "sendInitialModesCommand command is %s.\n", arg_command);
#endif

    gCarPlayInterfaceDelegate.initialModesCommand_f(arg_command);


    return true;
}


bool sendInitialChangeModes(const char* arg_screenBorrowId,
                            int arg_screenType,
                            int arg_screenPriority,
                            int arg_screenTake,
                            int arg_screenBorrow,
                            const char* arg_audioBorrowId,
                            int arg_audioType,
                            int arg_audioPriority,
                            int arg_audioTake,
                            int arg_audioBorrow,
                            int arg_phone,
                            int arg_speech,
                            int arg_turnByTurn,
                            int arg_screenPermanentEntity,
                            int arg_screenPermanentTakeConstraint,
                            int arg_screenPermanentBorrowConstraint,
                            int arg_audioPermanentEntity,
                            int arg_audioPermanentTakeConstraint,
                            int arg_audioPermanentBorrowConstraint) {
    gCarPlayInterfaceDelegate.initialChangeModes_f(arg_screenBorrowId, arg_screenType, arg_screenPriority, arg_screenTake,
                                                   arg_screenBorrow,
                                                   arg_audioBorrowId, arg_audioType, arg_audioPriority, arg_audioTake, arg_audioBorrow, arg_phone, arg_speech,
                                                   arg_turnByTurn,
                                                   arg_screenPermanentEntity, arg_screenPermanentTakeConstraint, arg_screenPermanentBorrowConstraint,
                                                   arg_audioPermanentEntity, arg_audioPermanentTakeConstraint, arg_audioPermanentBorrowConstraint);


    return true;
}


bool sendChangeModes(const char* arg_screenBorrowId,
                     int arg_screenType,
                     int arg_screenPriority,
                     int arg_screenTake,
                     int arg_screenBorrow,
                     const char* arg_audioBorrowId,
                     int arg_audioType,
                     int arg_audioPriority,
                     int arg_audioTake,
                     int arg_audioBorrow,
                     int arg_phone,
                     int arg_speech,
                     int arg_turnByTurn) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendChangeModes.\n");
#endif

    gCarPlayInterfaceDelegate.changeMode_f(arg_screenBorrowId, arg_screenType, arg_screenPriority, arg_screenTake,
                                           arg_screenBorrow,arg_audioBorrowId, arg_audioType, arg_audioPriority,
                                           arg_audioTake, arg_audioBorrow, arg_phone, arg_speech, arg_turnByTurn);


    return true;
}


bool sendChangeResourceMode(const char* arg_borrowID,
                            int arg_resourceId,
                            int arg_type,
                            int arg_priority,
                            int arg_takeConstraint,
                            int arg_borrowConstraint) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendChangeResourceMode borrowId is %s type is %d.\n", arg_borrowID, arg_type);
#endif

    gCarPlayInterfaceDelegate.changeResourceMode_f(arg_borrowID, arg_resourceId, arg_type, arg_priority, arg_takeConstraint,
                                                   arg_borrowConstraint);


    return true;
}


bool sendChangeAppState(int arg_phone,
                        int arg_speech,
                        int arg_turnByTurn) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendChangeAppState phone is %d speech is %d.\n", arg_phone, arg_speech);
#endif

    gCarPlayInterfaceDelegate.changeAppState_f(arg_speech, arg_phone, arg_turnByTurn);


    return true;
}


bool sendRequestUi() {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendRequestUi.\n");
#endif

    gCarPlayInterfaceDelegate.requestUi_f();


    return true;
}


bool sendRequestUiWithUrl(const char* arg_url) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendRequestUiWithUrl %s.\n", arg_url);
#endif

    gCarPlayInterfaceDelegate.requestUiWithUrl_f(arg_url);

    return true;
}


bool sendSetNightMode(int arg_nightMode) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendSetNightMode mode is %d.\n", arg_nightMode);
#endif

    gCarPlayInterfaceDelegate.setNightMode_f(arg_nightMode);


    return true;
}


bool sendUiAppearanceUpdate(int arg_mode,
                            int arg_setting) {
    gCarPlayInterfaceDelegate.uiAppearanceUpdate_f(arg_mode, arg_setting);

    return true;
}


bool sendSetLimitedUi(int arg_limitUi) {
    gCarPlayInterfaceDelegate.setLimitedUI_f(arg_limitUi);

    return true;
}


bool sendRequestSiriAction(int arg_action) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendRequestSiriAction action is %d.\n", arg_action);
#endif

    gCarPlayInterfaceDelegate.requestSiriAction_f(arg_action);


    return true;
}


bool sendTouchScreenUpdate(bool arg_press,
                           uint16_t arg_x,
                           uint16_t arg_y) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendTouchScreenUpdate press is %d (%d, %d).\n", arg_press, arg_x, arg_y);
#endif

    gCarPlayInterfaceDelegate.touchScreenUpdate_f(arg_press, arg_x, arg_y);


    return true;
}


bool sendTelephonyUpdate(int arg_index) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendTelephonyUpdate index: %d\n", arg_index);
#endif

    gCarPlayInterfaceDelegate.telephonyUpdate_f(arg_index);

    return true;
}


bool sendMediaButtonUpdate(int arg_index) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendMediaButtonUpdate index: %d\n", arg_index);
#endif

    gCarPlayInterfaceDelegate.mediaButtonUpdate_f(arg_index);

    return true;
}

bool sendKnobUpdate(char     selectButtonPressed,
                    char    homeButtonPressed,
                    char    backButtonPressed,
                    double  x,
                    double  y,
                    char    wheelPositionRelative) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendKnobUpdate buttonPressed = %d, homePressed = %d, backPressed = %d, x = %lf , y = %lf, wheel = %c\n",
            selectButtonPressed, homeButtonPressed, backButtonPressed, x, y, wheelPositionRelative);
#endif

    gCarPlayInterfaceDelegate.knob_f(selectButtonPressed, homeButtonPressed, backButtonPressed, x, y, wheelPositionRelative);

    return true;

}


OSStatus sendForceKeyFrame() {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendForceKeyFrame");
#endif

    gCarPlayInterfaceDelegate.forceKeyFrame_f();
    return true;
}


void handleSessionCreated() {
    //    airplay_emit_handle_session_created( gAirPlaySkeletonObject );
}


void handleSessionStarted(const char* arg_macAddress) {


    //    airplay_emit_handle_session_started( gAirPlaySkeletonObject, arg_macAddress );
	 gUserInterfaceDelegate.userCarPlaySessionStart_f();
}


void handleSessionFinalized() {

#if (DEBUG_CI)
        ci_ulog(kLogLevelNotice, "send a message ( AirPlaySessioninalized ) to platform.\n");
#endif
        gUserInterfaceDelegate.userCarPlaySessionStop_f();

    //    airplay_emit_handle_session_finalized( gAirPlaySkeletonObject );
}

void handleCarPlayExit() {
#if (DEBUG_CI)
     ci_ulog(kLogLevelNotice, "send a message ( carplay exit ) to platform.\n");
#endif
     gUserInterfaceDelegate.userAirPlaySessionEnd_f();

}

int handleCarPlaySwitchUsbModel(int mode) {
#if (DEBUG_CI)
         ci_ulog(kLogLevelNotice, "send a message ( carplay switch 2 usb model ) to platform.\n");
#endif
    gUserInterfaceDelegate.userSwitchUsbMode_f(mode);
    return -1;
}

char* handleMfiDevName() {
#if (DEBUG_CI)
             ci_ulog(kLogLevelNotice, "send a message ( handleMfiDevName ) to platform.\n");
#endif


    return NULL;
}

void handleSessionModesChanged(int arg_screen,
                               int arg_permScreen,
                               int arg_audio,
                               int arg_permAudio,
                               int arg_speechEntity,
                               int arg_speechMode,
                               int arg_phoneCall,
                               int arg_turnByTurn) {
    //    airplay_emit_handle_session_modes_changed( gAirPlaySkeletonObject, arg_screen, arg_permScreen, arg_audio, arg_permAudio,
    //        arg_speechEntity, arg_speechMode, arg_phoneCall, arg_turnByTurn );
	gUserInterfaceDelegate.userModesChanged_f(arg_screen,arg_permScreen,arg_audio,arg_permAudio,arg_speechEntity,arg_speechMode,arg_phoneCall,arg_turnByTurn);
}


void handleSessionRequestUi() {

	gUserInterfaceDelegate.userRequestUI_f();
    //   airplay_emit_handle_session_request_ui( gAirPlaySkeletonObject );
}


void handleSessionDuckAudio(double arg_durationSecs,
                            double arg_volume) {

	gUserInterfaceDelegate.userAirPlayDuckAudio_f(arg_durationSecs,arg_volume);
    //    airplay_emit_handle_session_duck_audio( gAirPlaySkeletonObject, arg_durationSecs, arg_volume );
}


void handleSessionUnduckAudio(double arg_durationSecs) {

	gUserInterfaceDelegate.userAirPlayUnduckAudio_f(arg_durationSecs);
    //    airplay_emit_handle_session_unduck_audio( gAirPlaySkeletonObject, arg_durationSecs );
}


void handleSessionControlDisableBluetooth(const char* arg_address) {
	gUserInterfaceDelegate.userDisableBluetooth_f();

    //    airplay_emit_handle_session_control_disable_bluetooth( gAirPlaySkeletonObject, arg_address );
}


void handleScreenStreamStart() {
	gUserInterfaceDelegate.userAirPlayScreenStreamStart_f();
    //   airplay_emit_handle_screen_stream_start( gAirPlaySkeletonObject );
}


void handleScreenStreamStop() {

	gUserInterfaceDelegate.userAirPlayScreenStreamStop_f();
    //    airplay_emit_handle_screen_stream_stop( gAirPlaySkeletonObject );
}


void handleScreenStreamDataProc(const uint8_t* buf, int len) {

	gUserInterfaceDelegate.userAirPlayScreenStreamData_f(buf,len);
}


void handleAudioStreamStart(const char* audioType,
                            int handle,
                            int streamType,
                            int rate,
                            int bits,
                            int channels,
                            void* inStream) {

	gUserInterfaceDelegate.userAirPlayAudioStreamStart_f(handle,streamType,rate,bits,channels,inStream);
    //    airplay_emit_handle_audio_stream_start( gAirPlaySkeletonObject, arg_audioType, arg_auxiliary );
}


void handleAudioStreamStop(int handle, int type) {

	gUserInterfaceDelegate.userAirPlayAudioStreamStop_f(handle,type);

    //airplay_emit_handle_audio_stream_stop( gAirPlaySkeletonObject, arg_audioType, arg_auxiliary );
}


//=================================================================================================================
// interface com.hhh.iap2, method client to server
//=================================================================================================================
bool sendResetMusicList(void) {
    giAP2InterfaceDelegate.sendResetMusicList_f();

    return true;
}


bool sendSelectMusicList(int arg_index) {
    giAP2InterfaceDelegate.sendSelectMusicList_f(arg_index);

    return true;
}


bool sendBackMusicList() {
    giAP2InterfaceDelegate.sendBackMusicList_f();

    return true;
}


bool sendGetMusicList(int arg_index,
                      int arg_count) {
    giAP2InterfaceDelegate.sendGetMusicPlayList_f(arg_index, arg_count);

    return true;
}


bool sendSelectMusicPlayList(int arg_index) {
    giAP2InterfaceDelegate.sendSelectMusicPlayList_f(arg_index);

    return true;
}


bool sendOobBtPairingCompletionInformation(int arg_resultCode) {
    giAP2InterfaceDelegate.sendOobBtPairingCompletionInformation_f(arg_resultCode);

    return true;
}


bool sendBluetoothPairingAccessoryInformation(char* PairingDataP192,
                                              char* PairingDataP256) {
    giAP2InterfaceDelegate.sendBluetoothPairingAccessoryInformation_f(PairingDataP192, PairingDataP256);

    return true;
}


bool sendBluetoothPairingStatus(int arg_success,
                                const char* arg_cancelReason) {
    giAP2InterfaceDelegate.sendBluetoothPairingStatus_f(arg_success, arg_cancelReason);

    return true;
}


bool sendRfcommData(char* arg_ptr,
                    int arg_length) {
    giAP2InterfaceDelegate.sendRfcommData_f(arg_ptr, arg_length);

    return true;
}


bool sendStartCallStateUpdates(void) {
#if (DEBUG_CI )
    ci_ulog(kLogLevelNotice, "sendStartCallStateUpdates!\n");
#endif

    return true;
}


bool sendWifiInformation(const char* arg_ssid,
                         const char* arg_passphrase,
                         int arg_securityType,
                         uint8_t arg_channel) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendWifiInformation ssid: %s, passphrase: %s.\n", arg_ssid, arg_passphrase);
#endif

    giAP2InterfaceDelegate.sendWiFiInformation_f(arg_ssid, arg_passphrase, arg_securityType, arg_channel);

    return true;
}


void setBtMacAddr(char* arg_addr) {

#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "setBtMacAddr=%2x:%2x:%2x:%2x:%2x:%2x\n", arg_addr[0], arg_addr[1], arg_addr[2], arg_addr[3],
            arg_addr[4], arg_addr[5]);
#endif

    giAP2InterfaceDelegate.setMacAddr_f(arg_addr);
}


bool sendSetup(int arg_type,
               uint16_t arg_vendorId,
               uint16_t arg_productId,
               const char* arg_address,
               int arg_same) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "iAP2 setup iAP2TransportType %d, VendorID %02x, ProductID %02x, MAC address %s.\n", arg_type,
            arg_vendorId, arg_productId, arg_address);
#endif

    giAP2InterfaceDelegate.setup_f(arg_type, arg_vendorId, arg_productId, arg_address, arg_same);


    return true;
}


bool sendStartSession(int arg_allow) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendStartSession allow: %d.\n", arg_allow);
#endif

    giAP2InterfaceDelegate.sendCarPlayStartSession_f(arg_allow);
    return true;
}


bool sendNmeaSentence(const char* arg_nmeaSentence) {
    giAP2InterfaceDelegate.sendNmeaSentence_f(arg_nmeaSentence);
    return true;
}


//=================================================================================================================
// interface com.hhh.iap2, signal server to client
//=================================================================================================================
void handleCallStateUpdate(const char* arg_remoteId,
                           const char* arg_displayName,
                           int arg_status,
                           int arg_direction) {
	gUserInterfaceDelegate.userCallStateUpdate_f(arg_remoteId,arg_displayName,arg_status,arg_direction,NULL,NULL,NULL,0);
    // iap2_emit_handle_call_state_update( giAP2SkeletonObject, arg_remoteId, arg_displayName, arg_status, arg_direction );
}


void handleNowPlayingUpdatePlaybackAttributes(int arg_playbackStatus,
                                              int arg_playbackShuffleMode,
                                              int arg_playbackRepeatMode,
                                              int arg_playbackElapsedTime) {
    //  iap2_emit_handle_now_playing_update_playback_attributes( giAP2SkeletonObject, arg_playbackStatus, arg_playbackShuffleMode, arg_playbackRepeatMode, arg_playbackElapsedTime );
}


void handleNowPlayingUpdateMediaItemAttributes(const char* arg_mediaItemTitle,
                                               const char* arg_mediaItemAlbumTitle,
                                               const char* arg_mediaItemArtist,
                                               int arg_mediaItemDuration) {
    //iap2_emit_handle_now_playing_update_media_item_attributes( giAP2SkeletonObject, arg_mediaItemTitle, arg_mediaItemAlbumTitle, arg_mediaItemArtist, arg_mediaItemDuration );
}


void handleDeviceInformationUpdate(const char* name) {
	gUserInterfaceDelegate.userDeviceInformationUpdate_f(name,strlen(name));
}


void handleRfcommData(const char* data, int length) {
	gUserInterfaceDelegate.useriAP2WriteData_f(data,length);
}


void handleIdentifierNotificationUpdated(const char* arg_BluetoothTransportIdentifier,
                                         const char* arg_UsbTransportIdentifier) {
    //  iap2_emit_handle_identifier_notification_updated( giAP2SkeletonObject, arg_BluetoothTransportIdentifier, arg_UsbTransportIdentifier );
}


void handleCarPlayAvailable(int available) {
    //    iap2_emit_handle_car_play_available( giAP2SkeletonObject, arg_available );
    sendStartSession(available);
}


void handleRouteGuidanceUpdate(int arg_routeGuidanceState,
                               int arg_maneuverState,
                               const char* arg_currentRoadName,
                               const char* arg_destinationName,
                               const char* arg_distanceRemainingDisplayString,
                               int arg_distanceRemainingDisplayUnits,
                               const char* arg_distanceToNextManeuverDisplayStr,
                               int arg_distanceToNextManeuverDisplayUnits,
                               int arg_timeRemainingHours,
                               int arg_timeRemainingMinutes) {
    //  iap2_emit_handle_route_guidance_update( giAP2SkeletonObject, arg_routeGuidanceState, arg_maneuverState, arg_currentRoadName, arg_destinationName, arg_distanceRemainingDisplayString,
    //        arg_distanceRemainingDisplayUnits, arg_distanceToNextManeuverDisplayStr, arg_distanceToNextManeuverDisplayUnits,
    //        arg_timeRemainingHours, arg_timeRemainingMinutes );
}


void handleRouteGuidanceManeuverInformation(int arg_maneuverType,
                                            const char* arg_afterManeuverRoadName,
                                            int arg_drivingSide) {
    //iap2_emit_handle_route_guidance_maneuver_information( giAP2SkeletonObject, arg_maneuverType, arg_afterManeuverRoadName, arg_drivingSide );
}


void handleStartBluetoothPairing(const char* arg_deviceMacAddress) {
    //  iap2_emit_handle_start_bluetooth_pairing( giAP2SkeletonObject, arg_deviceMacAddress );
}


void handleOobBtPairingLinkKeyInformation(const char* arg_linkKey,
                                          int arg_length,
                                          const char* arg_deviceMacAddress) {
    /*    GVariantBuilder *   builder;
        GVariant *          value;

        builder = g_variant_builder_new( G_VARIANT_TYPE("ay") );

        for( int i = 0; i < arg_length; i++ )
        {
            g_variant_builder_add( builder, "y", arg_linkKey[i] );
        }
        value = g_variant_new( "ay", builder );
        g_variant_builder_unref( builder );

        iap2_emit_handle_oob_bt_pairing_link_key_information( giAP2SkeletonObject, value, arg_length, arg_
        deviceMacAddress  );
    */
}


void handleMediaLibraryUpdateProgress(int arg_progress) {
    //    iap2_emit_handle_media_library_update_progress( giAP2SkeletonObject, arg_progress );
}


void handleMediaItemArtworkFileTransferIdentifier(const char* arg_path) {
    //    iap2_emit_handle_media_item_artwork_file_transfer_identifier( giAP2SkeletonObject, arg_path );
}


void handleMediaPlayListUpdate(int index) {
    //    iap2_emit_handle_media_play_list_update( giAP2SkeletonObject, index );
}


void handlePlaybackQueueListAvailable(int available) {
    //    iap2_emit_handle_playback_queue_list_available( giAP2SkeletonObject, available );
}

bool KnobUpdate(char     selectButtonPressed,
                    char    homeButtonPressed,
                    char    backButtonPressed,
                    double  x,
                    double  y,
                    char    wheelPositionRelative) {
#if (DEBUG_CI)
    ci_ulog(kLogLevelNotice, "sendKnobUpdate buttonPressed = %d, homePressed = %d, backPressed = %d, x = %lf , y = %lf, wheel = %c\n",
            selectButtonPressed, homeButtonPressed, backButtonPressed, x, y, wheelPositionRelative);
#endif

    gCarPlayInterfaceDelegate.knob_f(selectButtonPressed, homeButtonPressed, backButtonPressed, x, y, wheelPositionRelative);

    return true;

}


void handlePlayListInfo(const char* arg_ptr, int arg_length) {
    /*GVariantBuilder *   builder;
    GVariant *          value;

    builder = g_variant_builder_new( G_VARIANT_TYPE("ay") );

    for( int i = 0; i < arg_length; i++ )
    {
        g_variant_builder_add( builder, "y", arg_ptr[i] );
    }
    value = g_variant_new( "ay", builder );
    g_variant_builder_unref( builder );

    iap2_emit_handle_play_list_info( giAP2SkeletonObject, value, arg_length );
    */
}


